home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Communications / pcomm / Source / port.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  10.3 KB  |  449 lines

  1. /*
  2.  * Routines to get or release a TTY port.
  3.  */
  4.  
  5. #define MAX_PID    30000
  6. #define TRUE    1
  7. #define FALSE    0
  8.  
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include "config.h"
  12. #include "dial_dir.h"
  13. #include "modem.h"
  14.  
  15. #ifdef BSD
  16. #include <sys/file.h>
  17. #else /* BSD */
  18. #include <fcntl.h>
  19. #endif /* BSD */
  20.  
  21. #ifdef UNIXPC
  22. #include <sys/phone.h>
  23. #endif /* UNIXPC */
  24.  
  25. #ifdef XENIX_LOCKS
  26. #include <ctype.h>
  27. #endif /* XENIX_LOCKS */
  28.  
  29. #ifdef SVR4_LOCKS
  30. #include <sys/types.h>
  31. #include <sys/stat.h>
  32. #include <sys/mkdev.h>
  33. #endif /* SVR4_LOCKS */
  34.  
  35. static int getty_status = 0;
  36. static char *lock_path = NULL;
  37. /*
  38.  * Finds a free (or requested) serial port.  Creates a lock file to hold
  39.  * for our use.  Loads the modem database.  A non-zero return code means
  40.  * all ports (or the requested port) are busy.
  41.  */
  42.  
  43. int
  44. get_port()
  45. {
  46.     extern int fd;
  47.     register int i;
  48.     int j, k, lfd, list[NUM_TTY], cmask, is_dev, progpid;
  49.     static int set_getty(), checklock(), ck_speed();
  50.     char file[80], buf[80], message[80], *str_rep(), *last_c;
  51.     void error_win(), line_set(), release_port(), send_str();
  52.  
  53.     is_dev = chk_script(dir->script[dir->d_cur]);
  54.     /*
  55.      * If you already have a port, see if it is good enough for the
  56.      * current request.
  57.      */
  58. #ifdef KEEP_PORT
  59.     if (fd != -1) {
  60.         if (!strcmp(dir->script[dir->d_cur], modem->tty[modem->t_cur])
  61.          || (!is_dev && ck_speed(modem->t_cur, dir->baud[dir->d_cur]))){
  62.             /*
  63.              * Reset the line because the baud rate (or other
  64.              * parameters) may have changed.
  65.              */
  66.             line_set();
  67.             return(0);
  68.         }
  69.     }
  70. #endif /* KEEP_PORT */
  71.  
  72.     release_port(VERBOSE);
  73.  
  74.     list[0] = -1;
  75.     /*
  76.      * See if you want a specific TTY port.  If the script field in the
  77.      * dialing directory is a valid device name, then use that TTY.
  78.      */
  79.     if (is_dev) {
  80.         for (i=0; i<modem->t_entries; i++) {
  81.                     /* and it exists in modem database */
  82.             if (!strcmp(dir->script[dir->d_cur], modem->tty[i])) {
  83.                 list[0] = i;
  84.                 list[1] = -1;
  85.                 break;
  86.             }
  87.         }
  88.                     /* oops... we don't know that port */
  89.         if (list[0] == -1) {
  90.             sprintf(message, "Device \"%s\" in the script field doesn't exist in", dir->script[dir->d_cur]);
  91.             sprintf(buf, "modem/TTY database \"%s\"", modem->m_path);
  92.             error_win(0, message, buf);
  93.             return(1);
  94.         }
  95.     }
  96.  
  97.     /*
  98.      * Create a list of acceptable TTYs.  It searches the modem database
  99.      * for the requested baud rate.
  100.      */
  101.     k = 0;
  102.     if (list[0] == -1) {
  103.         for (i=0; i<modem->t_entries; i++) {
  104.                     /* skip ports with no modems */
  105.             if (!strcmp(modem->tname[i], "DIRECT"))
  106.                 continue;
  107.  
  108.                     /* can handle requested baud rate? */
  109.             if (ck_speed(i, dir->baud[dir->d_cur]))
  110.                 list[k++] = i;
  111.         }
  112.                     /* the end of list marker */
  113.         list[k] = -1;
  114.     }
  115.                     /* empty list? */
  116.     if (list[0] == -1) {
  117.         sprintf(message, "No modem at a %d baud rating exists in the", dir->baud[dir->d_cur]);
  118.         sprintf(buf, "modem database \"%s\"", modem->m_path);
  119.         error_win(0, message, buf);
  120.         return(1);
  121.     }
  122.                     /* check the list for a free port */
  123.     i = 0;
  124.     while (list[i] != -1) {
  125.                     /* create a lock file name */
  126. #ifdef SVR4_LOCKS
  127.         struct stat sbuf;
  128.         sprintf(buf, "/dev/%s", modem->tty[list[i]]);
  129.         stat(buf, &sbuf);
  130.         sprintf(file, "%s/LK.%03d.%03d.%03d", LOCK_DIR, major(sbuf.st_dev), major(sbuf.st_rdev), minor(sbuf.st_rdev));
  131. #else /* SVR4_LOCKS */
  132.         sprintf(file, "%s/LCK..%s", LOCK_DIR, modem->tty[list[i]]);
  133. #endif /* SVR4_LOCKS */
  134.  
  135. #ifdef XENIX_LOCKS
  136.         last_c = file + strlen(file)-1;
  137.         if (isupper(*last_c))
  138.             *last_c = tolower(*last_c);
  139. #endif /* XENIX_LOCKS */
  140.  
  141. #ifdef DEBUG
  142.         fprintf(stderr, "get_port: checking '/dev/%s'\n", modem->tty[list[i]]);
  143. #endif /* DEBUG */
  144.  
  145.                     /* no lock exists or it is dead */
  146.         if (checklock(file)) {
  147.             getty_status = set_getty(modem->tty[list[i]], FALSE);
  148.  
  149.             cmask = umask(0);
  150.             if ((lfd = open(file, O_CREAT|O_EXCL|O_WRONLY, 0666)) < 0) {
  151.                 if (getty_status)
  152.                     set_getty(modem->tty[list[i]], TRUE);
  153.                 sprintf(buf, "\"%s\"", file);
  154.                 error_win(0, "Can't create the lockfile", buf);
  155.                 return(1);
  156.             }
  157.             umask(cmask);
  158. #ifdef ASCII_PID
  159.             sprintf(buf, "%10d\n", getpid());
  160.             write(lfd, buf, 11);
  161. #else /* ASCII_PID */
  162.             progpid = getpid();
  163.             write(lfd, (char *) &progpid, sizeof(int));
  164. #endif /* ASCII_PID */
  165.             close(lfd);
  166.                     /* store the new values */
  167.             lock_path = str_rep(lock_path, file);
  168.             modem->t_cur = list[i];
  169.  
  170.                     /* load the modem data base */
  171.             modem->m_cur = -1;
  172.             for (j=0; j<modem->m_entries; j++) {
  173.                 if (!strcmp(modem->tname[modem->t_cur], modem->mname[j])) {
  174.                     modem->m_cur = j;
  175.                     break;
  176.                 }
  177.             }
  178.             if (modem->m_cur == -1) {
  179.                 sprintf(buf, "Modem name \"%s\" in TTY database",
  180.                  modem->tname[modem->t_cur]);
  181.                 error_win(0, buf, "does not exist in modem database");
  182.                 modem->t_cur = -1;
  183.                 return(1);
  184.             }
  185.  
  186.                     /* open the device (ignore DCD) */
  187.             sprintf(buf, "/dev/%s", modem->tty[modem->t_cur]);
  188.             if ((fd = open(buf, O_RDWR|O_NDELAY)) < 0) {
  189.                 if (getty_status)
  190.                     set_getty(modem->tty[modem->t_cur], TRUE);
  191.                 sprintf(file, "Can't open port \"%s\" for read and write", buf);
  192.                 error_win(0, file, "");
  193.                 modem->m_cur = -1;
  194.                 modem->t_cur = -1;
  195.                 return(1);
  196.             }
  197.                     /* change line settings */
  198.             line_set();
  199.                     /* ...just to be sure */
  200.             close(open(buf, O_RDWR));
  201.  
  202.                     /* turn off the O_NDELAY setting */
  203.             tty_noblock(fd, FALSE);
  204.  
  205.                     /* initialize the modem */
  206.             send_str(modem->init[modem->m_cur], SLOW);
  207.             return(0);
  208.         }
  209.         i++;
  210.     }
  211.     error_win(0, "All ports are busy now, try again later", "");
  212.     return(1);
  213. }
  214.  
  215. /*
  216.  * Release the port.  Closes the file descriptor and removes the
  217.  * lock file
  218.  */
  219.  
  220. void
  221. release_port(verbose)
  222. int verbose;
  223. {
  224.     extern int fd;
  225.     extern char *null_ptr;
  226.     char buf[80];
  227.     void free_ptr(), hang_up(), reset_line(), error_win();
  228.  
  229.     /*
  230.      * The modem structure can't be guaranteed to exist yet.  For example,
  231.      * an error in reading one of the other support files would cause
  232.      * this routine to be used before the MODEM structure gets allocated.
  233.      */
  234.     if (modem == NULL)
  235.         return;
  236.                     /* close the port */
  237.     if (fd != -1) {
  238.         tty_flush(fd, 2);
  239.         /*
  240.          * Since HUPCL is set, the close() should drop the DTR and
  241.          * hang up the modem (provided you've got the modem to
  242.          * respond to DTR).  Since this is not guaranteed, we send
  243.          * the hang_up string first.
  244.          */
  245.         hang_up(verbose);
  246.         reset_line();
  247.         close(fd);
  248.     }
  249.                     /* remove the lock */
  250.     if (lock_path != NULL && *lock_path != '\0') {
  251.         if (unlink(lock_path)) {
  252.             sprintf(buf, "\"%s\"", lock_path);
  253.             error_win(0, "Can't remove the lock file", buf);
  254.         }
  255.         free_ptr(lock_path);
  256.         lock_path = null_ptr;
  257.     }
  258.                     /* turn the getty back on? */
  259.     if (getty_status && modem->t_cur != -1)
  260.         set_getty(modem->tty[modem->t_cur], TRUE);
  261.                     /* clean up the structure */
  262.     fd = -1;
  263.     modem->m_cur = -1;
  264.     modem->t_cur = -1;
  265.     return;
  266. }
  267.  
  268. /*
  269.  * Turn the /etc/getty on or off for the specified port.  A non-zero return
  270.  * code means that the getty was on.  Systems with uugetty() or dedicated
  271.  * dialout ports won't need this routine.
  272.  */
  273.  
  274. /* ARGSUSED */
  275. static int
  276. set_getty(tty, on)
  277. char *tty;
  278. int on;
  279. {
  280. #ifdef UNIXPC
  281.     int i, ret_code;
  282.     char buf[40];
  283.     unsigned int sleep();
  284.                     /* the last three characters */
  285.     i = strlen(tty) -3;
  286.  
  287.     ret_code = 0;
  288.     if (on) {
  289.         sprintf(buf, "setgetty %s 1", tty+i);
  290.         system(buf);
  291.     }
  292.     else {
  293.         sprintf(buf, "setgetty %s 0", tty+i);
  294.         if (system(buf) == 512)
  295.             ret_code++;
  296.         sleep(1);
  297.     }
  298.     return(ret_code);
  299. #else /* UNIXPC */
  300.     /*
  301.      * If you don't have one of these cute little routines, you
  302.      * might wanna write one.  It should check for an existing lock
  303.      * file, edit the /etc/inittab file, and issue an init -q.
  304.      * The return code should tell if there was a getty or not.
  305.      * Obviously the program would be suid to root.
  306.      */
  307.     return(0);
  308. #endif /* UNIXPC */
  309. }
  310.  
  311. /*
  312.  * Check the lock file for a valid pid value.  Error conditions such
  313.  * as not being able to open the lock file or not being able to interpret
  314.  * the contents of the lock file cause the code to assume that the lock
  315.  * file is valid.  Let the user worry about weird special cases.  A 
  316.  * non-zero return code means the lock is dead or doesn't exist.
  317.  */
  318.  
  319. static int
  320. checklock(lockfile)
  321. char *lockfile;
  322. {
  323.     extern int errno;
  324.     int lfd, lckpid, n;
  325.     unsigned int sleep();
  326.     char buf[40];
  327.                     /* doesn't exist */
  328.     if (access(lockfile, 0))
  329.         return(1);
  330.                     /* can't open the lock file */
  331.     if ((lfd = open(lockfile, 0)) < 0)
  332.         return(0);
  333.  
  334. #ifdef ASCII_PID
  335.     if ((n = read(lfd, buf, 40)) <= 0) {
  336.         close(lfd);
  337.         return(0);
  338.     }
  339.     close(lfd);
  340.     buf[n--] = '\0';
  341.     lckpid = atoi(buf);
  342. #else /* ASCII_PID */
  343.     if (read(lfd, (char *) &lckpid, sizeof(int)) != sizeof(int)) {
  344.         close(lfd);
  345.         return(0);
  346.     }
  347.     close(lfd);
  348. #endif /* ASCII_PID */
  349.                     /* invalid pid? */
  350.     if (lckpid <= 0 || lckpid > MAX_PID)
  351.         return(0);
  352.  
  353.     if ((kill(lckpid, 0) == -1) && (errno == ESRCH)) {
  354.         /*
  355.          * If the kill was unsuccessful due to an ESRCH error,
  356.          * that means the process is no longer active and the
  357.          * lock file can be safely removed.
  358.          */
  359.         unlink(lockfile);
  360.         sleep(1);
  361.         return(1);
  362.     }
  363.     /*
  364.      * If the kill() was successful, that means the process must
  365.      * still be active.
  366.      */
  367.     return(0);
  368. }
  369.  
  370. /*
  371.  * Check to see if the desired baud rate can be handled by the modem.
  372.  * Uses the connect strings to make this determination.  The first
  373.  * argument is the index into the TTY database.  A non-zero return code
  374.  * means "yes it can".
  375.  */
  376.  
  377. static int
  378. ck_speed(tty, baud)
  379. int tty, baud;
  380. {
  381.     int i, mod;
  382.     char buf[60];
  383.     void error_win();
  384.                     /* find the modem database */
  385.     mod = -1;
  386.     for (i=0; i<modem->m_entries; i++) {
  387.         if (!strcmp(modem->mname[i], modem->tname[tty])) {
  388.             mod = i;
  389.             break;
  390.         }
  391.     }
  392.     if (mod == -1) {
  393.         sprintf(buf, "Modem name \"%s\" in TTY database", modem->tname[tty]);
  394.         error_win(1, buf, "does not exist in modem database");
  395.     }
  396.  
  397. #ifdef DEBUG
  398.     fprintf(stderr, "ck_speed: checking modem \"%s\" for %d baud\n", modem->mname[mod], baud);
  399. #endif /* DEBUG */
  400.  
  401.     switch (baud) {
  402.         case 300:
  403.             if (*modem->con_3[mod] != '\0')
  404.                 return(1);
  405.             break;
  406.         case 1200:
  407.             if (*modem->con_12[mod] != '\0')
  408.                 return(1);
  409.             break;
  410.         case 2400:
  411.             if (*modem->con_24[mod] != '\0')
  412.                 return(1);
  413.             break;
  414.         case 4800:
  415.             if (*modem->con_48[mod] != '\0')
  416.                 return(1);
  417.             break;
  418.         case 9600:
  419.             if (*modem->con_96[mod] != '\0')
  420.                 return(1);
  421.             break;
  422.         case 19200:
  423.             if (*modem->con_192[mod] != '\0')
  424.                 return(1);
  425.             break;
  426.     }
  427.     return(0);
  428. }
  429.  
  430. /*
  431.  * Check to see if the script field is a valid device name.
  432.  */
  433.  
  434. chk_script(script)
  435. char *script;
  436. {
  437.     char buf[80], *strcpy(), *strcat();
  438.  
  439.     if (*script == '\0')
  440.         return(0);
  441.  
  442.     strcpy(buf, "/dev/");
  443.     strcat(buf, script);
  444.  
  445.     if (!access(buf, 0))
  446.         return(1);
  447.     return(0);
  448. }
  449.